package com.xinqi.modules.discuss.notice.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xinqi.common.base.dto.CommonFileDTO;
import com.xinqi.common.base.enums.YesNoNumberEnum;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.response.R;
import com.xinqi.common.base.utils.spring.Spring;
import com.xinqi.common.core.enums.AssistantMessageTypeEnum;
import com.xinqi.common.core.enums.ClassRoleEnum;
import com.xinqi.common.core.enums.CourseContentTypeEnum;
import com.xinqi.common.core.enums.DiscussReleaseStatusEnum;
import com.xinqi.common.core.utils.MessageUtils;
import com.xinqi.common.mybatis.data.service.impl.BaseServiceImpl;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.modules.course.classmenu.client.ClassMenuClient;
import com.xinqi.modules.course.clazz.client.ClassStudentClient;
import com.xinqi.modules.course.clazz.dto.rsp.ClassStudentResultDTO;
import com.xinqi.modules.course.content.dto.client.CourseContentClient;
import com.xinqi.modules.course.content.dto.req.CourseContentCreateDTO;
import com.xinqi.modules.course.content.dto.req.CourseContentUpdateDTO;
import com.xinqi.modules.course.content.dto.rsp.CourseContentResultDTO;
import com.xinqi.modules.course.course.constant.CourseConstants;
import com.xinqi.modules.discuss.assistant.AssistantListen;
import com.xinqi.modules.discuss.notice.convert.CourseNoticeConvert;
import com.xinqi.modules.discuss.notice.domain.CourseNoticeEntity;
import com.xinqi.modules.discuss.notice.domain.CourseNoticeStudentLogEntity;
import com.xinqi.modules.discuss.notice.dto.req.*;
import com.xinqi.modules.discuss.notice.dto.rsp.CourseNoticeResultDTO;
import com.xinqi.modules.discuss.notice.dto.rsp.CourseNoticeStudentLogResultDTO;
import com.xinqi.modules.discuss.notice.mapper.CourseNoticeMapper;
import com.xinqi.modules.discuss.notice.service.CourseNoticeService;
import com.xinqi.modules.discuss.notice.service.CourseNoticeStudentLogService;
import com.xinqi.modules.stats.assistant.dto.rsp.AssistantTotalResultDTO;
import com.xinqi.modules.stats.assistant.dto.rsp.StudentInfoResultDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 课程公告主表服务层实现
 *
 * @author: yyzhen
 * @date: 2023/03/21
 */
@Log4j2
@Service("courseNoticeService")
@RequiredArgsConstructor
public class CourseNoticeServiceImpl extends BaseServiceImpl<CourseNoticeMapper, CourseNoticeEntity> implements CourseNoticeService {

    private final CourseNoticeConvert courseNoticeConvert;

    private final CourseNoticeStudentLogService courseNoticeStudentLogService;

    private final ClassStudentClient classStudentClient;
    private final ClassMenuClient classMenuClient;
    private final CourseContentClient courseContentClient;

    @Lazy
    private final AssistantListen assistantListen;

    /**
     * 分页查询 课程公告主表数据
     */
    @Override
    @DS("slaver")
    public PageInfo<CourseNoticeResultDTO> selectPage(Param pageable, CourseNoticeQueryDTO query) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(query.getClassId()), MessageUtils.message("course.discuss.classId.not.blank"));
        //排序
        if (CollectionUtils.isEmpty(pageable.getSorts())) {
            pageable.setSorts(new ArrayList<>());
        }
        pageable.getSorts().add("t1.is_top,desc");
        pageable.getSorts().add("t1.top_time,desc");
        pageable.getSorts().add("t1.release_time,desc");
        //已发布
        if (Objects.equals(1, query.getType())) {
            query.setStatusList(Arrays.asList(DiscussReleaseStatusEnum.IN_PROGRESS.getCode(), DiscussReleaseStatusEnum.CLOSED.getCode()));
        }
        //未发布
        if (Objects.equals(2, query.getType())) {
            query.setStatusList(Arrays.asList(DiscussReleaseStatusEnum.UNPUBLISHED.getCode(), DiscussReleaseStatusEnum.SCHEDULED_RELEASE.getCode()));
            pageable.setSorts(new ArrayList<>());
            pageable.getSorts().add("t1.create_time,desc");
        }

        // 添加身份校验（如果是学生 操作者ID更新为 主学生ID）
        ClassStudentResultDTO student = R.as(classStudentClient.findVirtualClassStudentByClassIdAndUserId(query.getClassId(), query.getOperationUserId()));
        if (Objects.nonNull(student)) {
            query.setOperationUserId(student.getStudentId());
        }

        IPage<CourseNoticeResultDTO> pageData = this.baseMapper.selectPage1(Pages.page(pageable), query);
        return Pages.convert(pageData);
    }

    /**
     * 获取个人与公告的详情
     *
     * @param id
     * @param userId
     * @return
     */
    @Override
    public CourseNoticeResultDTO findByIdAndUserId(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));

        CourseNoticeEntity notice = this.baseMapper.selectById(id);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(notice), "公告不存在");

        ClassStudentResultDTO mainStudent = R.as(classStudentClient.findVirtualClassStudentByClassIdAndUserId(notice.getClassId(), userId));
        CourseNoticeResultDTO result = new CourseNoticeResultDTO();
        if (Objects.nonNull(mainStudent)) {
            result = Optional.ofNullable(this.baseMapper.selectByIdAndUserId(id, mainStudent.getStudentId(), CourseContentTypeEnum.NOTICE.getCode()))
                .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));
        } else {
            result = Optional.ofNullable(this.baseMapper.selectByIdAndUserId(id, userId, CourseContentTypeEnum.NOTICE.getCode()))
                .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));
        }

        //查看即已读
        courseNoticeStudentLogService.synchroRead(result, userId);

        return result;
    }

    /**
     * 新增 课程公告主表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insert(CourseNoticeCreateDTO dto) {
        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(dto.getClassId(), dto.getOperationUserId(), classRoles);

        CourseNoticeEntity entity = courseNoticeConvert.create(dto);
        //预设未发布
        entity.setStatus(YesNoNumberEnum.NO.getCode());
        LocalDateTime now = LocalDateTime.now();
        //校验当前时间在截止时间之前
        CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getEndTime()), MessageUtils.message("course.notice.now.before"));
        if (Objects.isNull(dto.getTiming())) {
            dto.setTiming(YesNoNumberEnum.NO.getCode());
        }
        //定时发布
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getTiming())) {
            CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getReleaseTime()), MessageUtils.message("course.notice.releasetime.not.blank"));
            //校验定时发布的发布时间在截止时间之前
            CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getReleaseTime()), MessageUtils.message("course.notice.releasetime.before"));
            CommonErrors.BAD_REQUEST.check(dto.getReleaseTime().isBefore(dto.getEndTime()), MessageUtils.message("course.notice.releasetime.before"));
            entity.setStatus(YesNoNumberEnum.NO.getCode());
            entity.setNowRelease(YesNoNumberEnum.NO.getCode());
        }
        //立即发布 -- 发布时校验当前时间在截止时间之前(发布时间即当前时间，前面有对截止时间做校验，故此出不做校验)
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getNowRelease())) {
            entity.setStatus(YesNoNumberEnum.YES.getCode());
            entity.setReleaseTime(now);
            entity.setTiming(YesNoNumberEnum.NO.getCode());
        }
        entity.setIsTop(YesNoNumberEnum.NO.getCode());
        entity.setIsDelete(YesNoNumberEnum.NO.getCode());
        entity.setCreateBy(dto.getOperationUserId());
        this.save(entity);
        this.synchroContentNotice(courseNoticeConvert.convert(entity));
        //同步学生设置
        courseNoticeStudentLogService.synchroStudent(entity);
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), entity.getNowRelease())) {
            //同步班级设置
            this.synchroClass(entity);
            //TODO 新奇助手发送消息
            Spring.committed(() -> assistantListen.releaseCourseContent(entity.getId(), AssistantMessageTypeEnum.NOTICE));
        }
        return true;
    }

    /**
     * 根据id修改 课程公告主表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateById(CourseNoticeUpdateDTO dto) {
        String message = MessageUtils.message("course.notice.update.success");
        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(dto.getClassId(), dto.getOperationUserId(), classRoles);

        LocalDateTime now = LocalDateTime.now();
        CourseNoticeEntity old = Optional.ofNullable(this.getById(dto.getId()))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));

        // 已弃用
        // classMenuClient.checkShowCourseContent(old.getClassId(), CourseContentTypeEnum.NOTICE);

        //已结束的不可以编辑
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), old.getStatus())) {
            CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getEndTime()), MessageUtils.message("course.notice.update.closed"));
        }
        //校验当前时间在截止时间之前
        CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getEndTime()), MessageUtils.message("course.notice.now.before"));
        CourseNoticeEntity entity = null;
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), old.getStatus())) {
            //进行中只可以修改截止时间
            entity = new CourseNoticeEntity();
            entity.setId(old.getId());
            entity.setEndTime(dto.getEndTime());
        } else {
            entity = courseNoticeConvert.update(dto);
            //定时发布
            if (Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getTiming())) {
                CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getReleaseTime()), MessageUtils.message("course.notice.releasetime.not.blank"));
                //校验定时发布的发布时间在截止时间之前
                CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getReleaseTime()), MessageUtils.message("course.notice.releasetime.before"));
                CommonErrors.BAD_REQUEST.check(dto.getReleaseTime().isBefore(dto.getEndTime()), MessageUtils.message("notice.discuss.releasetime.before"));
                entity.setNowRelease(YesNoNumberEnum.NO.getCode());
                message = MessageUtils.message("course.timing.message.success");
            }
            //非立即发布 -> 立即发布
            if (Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getNowRelease()) && Objects.equals(YesNoNumberEnum.NO.getCode(), old.getNowRelease())) {
                //发布时校验当前时间在截止时间之前
                CommonErrors.BAD_REQUEST.check(now.isBefore(dto.getEndTime()), MessageUtils.message("course.notice.now.before"));
                entity.setStatus(YesNoNumberEnum.YES.getCode());
                entity.setReleaseTime(now);
                entity.setTiming(YesNoNumberEnum.NO.getCode());
                //同步班级设置
                entity.setCreateBy(old.getCreateBy());
                entity.setUpdateBy(old.getUpdateBy());
                this.synchroClass(entity);
                message = MessageUtils.message("course.release.message.success");
            }
        }
        this.updateById(entity);
        this.synchroContentNotice(courseNoticeConvert.convert(entity));
        //TODO 先修改再发送
        if (Objects.equals(YesNoNumberEnum.NO.getCode(), old.getStatus()) && Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getNowRelease()) && Objects.equals(YesNoNumberEnum.NO.getCode(), old.getNowRelease())) {
            //发送消息助手
            assistantListen.releaseCourseContent(entity.getId(), AssistantMessageTypeEnum.NOTICE);
        }
        return message;
    }

    /**
     * 根据ID删除
     *
     * @Title:
     * @MethodName: remove
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/21 11:13
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean remove(CourseNoticeDeleteDTO dto) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getId()), MessageUtils.message("course.notice.id.not.blank"));

        CourseNoticeEntity entity = Optional.ofNullable(this.getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));
        // 校验板块是否被移除
        classMenuClient.checkShowCourseContent(entity.getClassId(), CourseContentTypeEnum.NOTICE);

        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(entity.getClassId(), dto.getOperationUserId(), classRoles);
        //同步删除主表
        courseContentClient.deleteCourseContentByIds(new ArrayList<>(Collections.singletonList(dto.getId())), CourseContentTypeEnum.NOTICE.getCode());
        //同步删除学生记录表
        courseNoticeStudentLogService.deleteByContentId(dto.getId());

        return this.deleteById(dto.getId());
    }

    /**
     * 根据ID批量删除
     *
     * @Title:
     * @MethodName: batchRemove
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/21 11:13
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchRemove(CourseNoticeDeleteDTO dto) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto.getIds()), MessageUtils.message("course.notice.id.not.blank"));
        List<CourseNoticeEntity> list = this.baseMapper.selectBatchIds(dto.getIds());
        if (CollectionUtils.isEmpty(list)) {
            return Boolean.TRUE;
        }
        Long classId = list.get(0).getClassId();
        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(classId, dto.getOperationUserId(), classRoles);
        boolean check = Boolean.TRUE;
        for (CourseNoticeEntity entity : list) {
            if (!Objects.equals(classId, entity.getClassId())) {
                check = Boolean.FALSE;
            }
            if (!check) {
                break;
            }
        }
        CommonErrors.BAD_REQUEST.check(check, MessageUtils.message("course.comment.is.not.permissions"));

        //同步删除主表
        courseContentClient.deleteCourseContentByIds(dto.getIds(), CourseContentTypeEnum.NOTICE.getCode());
        //同步删除学生记录表
        courseNoticeStudentLogService.deleteByContentIds(dto.getIds());

        return this.deletedByIds(dto.getIds());
    }

    /**
     * 置顶
     *
     * @Title:
     * @MethodName: isTop
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 10:36
     */
    @Override
    public Boolean isTop(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));
        CourseNoticeEntity entity = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));
        // 校验板块是否被移除
        classMenuClient.checkShowCourseContent(entity.getClassId(), CourseContentTypeEnum.NOTICE);

        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(entity.getClassId(), userId, classRoles);
        //未发布不可操作
        CommonErrors.BAD_REQUEST.check(Objects.equals(YesNoNumberEnum.YES.getCode(), entity.getStatus()), MessageUtils.message("course.notice.message.fail"));
        //置顶限制 -- 5条
        LambdaQueryWrapper<CourseNoticeEntity> queryWrapper = Queries.lambda();
        queryWrapper.eq(CourseNoticeEntity::getClassId, entity.getClassId())
            .eq(CourseNoticeEntity::getIsTop, YesNoNumberEnum.YES.getCode());
        CommonErrors.BAD_REQUEST.check(this.baseMapper.selectCount(queryWrapper) <= CourseConstants.CourseNoticeCount.TOP_NUM, MessageUtils.message("course.notice.top.valid"));
        entity.setIsTop(YesNoNumberEnum.YES.getCode());
        entity.setTopTime(LocalDateTime.now());
        return this.updateById(entity);
    }

    /**
     * 取消置顶
     *
     * @Title:
     * @MethodName: cancelTop
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 10:36
     */
    @Override
    public Boolean cancelTop(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));
        CourseNoticeEntity entity = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));

        classMenuClient.checkShowCourseContent(entity.getClassId(), CourseContentTypeEnum.NOTICE);

        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(entity.getClassId(), userId, classRoles);

        LambdaUpdateWrapper<CourseNoticeEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(CourseNoticeEntity::getIsTop, YesNoNumberEnum.NO.getCode())
            .set(CourseNoticeEntity::getTopTime, null)
            .eq(CourseNoticeEntity::getId, id);
        return this.update(wrapper);
    }

    /**
     * 发布
     *
     * @Title:
     * @MethodName: isRelease
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 10:36
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean isRelease(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));
        CourseNoticeEntity entity = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));

        //校验板块是否被移除
        classMenuClient.checkShowCourseContent(entity.getClassId(), CourseContentTypeEnum.NOTICE);

        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(entity.getClassId(), userId, classRoles);
        //未发布才可以发布
        CommonErrors.BAD_REQUEST.check(Objects.equals(YesNoNumberEnum.NO.getCode(), entity.getStatus()), MessageUtils.message("course.notice.is.not.unpublished"));
        LocalDateTime now = LocalDateTime.now();
        //发布时校验当前时间在截止时间之前
        CommonErrors.BAD_REQUEST.check(now.isBefore(entity.getEndTime()), MessageUtils.message("course.notice.now.before"));

        entity.setStatus(YesNoNumberEnum.YES.getCode());
        entity.setReleaseTime(now);
        //定时发布一键发布
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), entity.getTiming())) {
            entity.setTiming(YesNoNumberEnum.NO.getCode());
        }
        boolean flag = this.updateById(entity);
        if (flag) {
            //同步班级设置
            this.synchroClass(entity);
            //同步主表
            this.synchroContentNotice(courseNoticeConvert.convert(entity));
            //TODO 发送新奇助手消息
            assistantListen.releaseCourseContent(entity.getId(), AssistantMessageTypeEnum.NOTICE);
        }
        return flag;
    }

    /**
     * 发布V2版本
     * @Title:
     * @MethodName: isRelease
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 10:36
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean isReleaseV2(Long id, Long userId) {

        CourseNoticeEntity entity = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));
        CommonErrors.BAD_REQUEST.check(Objects.equals(YesNoNumberEnum.NO.getCode(), entity.getStatus()),
            MessageUtils.message("course.notice.release.valid"));

        //校验板块是否被移除
        classMenuClient.checkShowCourseContent(entity.getClassId(), CourseContentTypeEnum.NOTICE);

        //校验为本班老师
        List<ClassRoleEnum> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.TEACHER);
        classRoles.add(ClassRoleEnum.ASSISTANT);
        classStudentClient.checkRole(entity.getClassId(), userId, classRoles);
        //未发布才可以发布
        CommonErrors.BAD_REQUEST.check(Objects.equals(YesNoNumberEnum.NO.getCode(), entity.getStatus()), MessageUtils.message("course.notice.is.not.unpublished"));
        LocalDateTime now = LocalDateTime.now();
        //发布时校验当前时间在截止时间之前
        CommonErrors.BAD_REQUEST.check(now.isBefore(entity.getEndTime()), MessageUtils.message("course.notice.now.before"));

        entity.setStatus(YesNoNumberEnum.YES.getCode());
        entity.setReleaseTime(now);
        //定时发布一键发布
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), entity.getTiming())) {
            entity.setTiming(YesNoNumberEnum.NO.getCode());
        }
        boolean flag = this.updateById(entity);
        if (flag) {
            //同步班级设置
            this.synchroClass(entity);
            //同步主表
            this.synchroContentNotice(courseNoticeConvert.convert(entity));
            //TODO 发送新奇助手消息
            assistantListen.releaseCourseContent(entity.getId(), AssistantMessageTypeEnum.NOTICE);
        }
        return flag;
    }

    /**
     * 确认
     *
     * @Title:
     * @MethodName: confirm
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 16:17
     */
    @Override
    public Boolean confirm(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));
        CourseNoticeEntity old = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.notice.is.remove")));

        CommonErrors.BAD_REQUEST.check(!Objects.equals(DiscussReleaseStatusEnum.CLOSED.getCode(), old.getStatus()), MessageUtils.message("course.notice.update.closed"));
        //只有进行中才可以确认
        LocalDateTime now = LocalDateTime.now();
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), old.getStatus())) {
            if (now.isAfter(old.getEndTime())) {
                return true;
            }
        } else {
            return true;
        }

        // 获取用户信息
        ClassStudentResultDTO student = R.as(classStudentClient.findByStudentId(old.getClassId(), userId));
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), student.getIsTeacher())) {
            return true;
        }

        // 获取对应用户主学生信息
        ClassStudentResultDTO mainStudent = R.as(classStudentClient.findVirtualClassStudentByClassIdAndUserId(old.getClassId(), userId));

        CourseNoticeStudentLogEntity studentLogEntity = courseNoticeStudentLogService.selectByNoticeIdAndStudentId(id, mainStudent.getStudentId());
        if (Objects.isNull(studentLogEntity)) {
            //无数据 -- 已读已确认
            studentLogEntity = new CourseNoticeStudentLogEntity();
            studentLogEntity.setClassId(old.getClassId());
            studentLogEntity.setContentId(id);
            studentLogEntity.setStudentId(mainStudent.getStudentId());
            studentLogEntity.setIsRead(YesNoNumberEnum.YES.getCode());
            studentLogEntity.setIsConfirm(YesNoNumberEnum.YES.getCode());
            studentLogEntity.setConfirmTime(LocalDateTime.now());
            // 设置操作用户
            studentLogEntity.setCreateBy(student.getId());
            studentLogEntity.setOperaRelationship(student.getRelationship());
            return courseNoticeStudentLogService.save(studentLogEntity);
        } else {
            if (Objects.equals(YesNoNumberEnum.NO.getCode(), studentLogEntity.getIsConfirm())) {
                //有数据 -- 已确认
                studentLogEntity.setIsConfirm(YesNoNumberEnum.YES.getCode());
                studentLogEntity.setConfirmTime(LocalDateTime.now());
                // 设置操作用户
                studentLogEntity.setCreateBy(student.getId());
                studentLogEntity.setOperaRelationship(student.getRelationship());

                return courseNoticeStudentLogService.updateById(studentLogEntity);
            }
        }

        return true;
    }

    /**
     * 下载
     *
     * @Title:
     * @MethodName: download
     * [dto]
     * @Return java.util.List<java.lang.String>
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 17:07
     */
    @Override
    public List<String> download(CourseNoticeDownloadDTO dto) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto.getIds()), MessageUtils.message("course.notice.id.not.blank"));
        List<String> urls = new ArrayList<>();
        List<CourseNoticeEntity> entityList = this.baseMapper.selectBatchIds(dto.getIds());
        if (entityList != null && entityList.size() > 0) {
            for (CourseNoticeEntity entity : entityList) {
                if (entity.getFileurls() != null && StringUtils.isNotEmpty(entity.getFileurls())) {
                    List<CommonFileDTO> files = JSONArray.parseArray(entity.getFileurls(), CommonFileDTO.class);
                    if (files != null && files.size() > 0) {
                        files.forEach(item -> urls.add(item.getFileUrl()));
                    }
                }
                if (entity.getImages() != null && StringUtils.isNotEmpty(entity.getImages())) {
                    List<CommonFileDTO> images = JSONArray.parseArray(entity.getImages(), CommonFileDTO.class);
                    if (images != null && images.size() > 0) {
                        images.forEach(item -> urls.add(item.getFileUrl()));
                    }
                }
                if (entity.getVideos() != null && StringUtils.isNotEmpty(entity.getVideos())) {
                    List<CommonFileDTO> files = JSONArray.parseArray(entity.getVideos(), CommonFileDTO.class);
                    if (files != null && files.size() > 0) {
                        files.forEach(item -> urls.add(item.getFileUrl()));
                    }
                }
            }
        }
        return urls;
    }

    /**
     * 查看学生已读确认列表
     *
     * @Title:
     * @MethodName: selectStudentLogPage
     * [pageable, query]
     * @Return com.xinqi.common.base.response.PageInfo<com.xinqi.modules.discuss.notice.dto.rsp.CourseNoticeStudentLogResultDTO>
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 14:25
     */
    @Override
    public PageInfo<CourseNoticeStudentLogResultDTO> selectStudentLogPage(Param pageable, CourseNoticeStudentLogQueryDTO query) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(query.getContentId()), MessageUtils.message("course.notice.id.not.blank"));
        return courseNoticeStudentLogService.selectPage(pageable, query);
    }

    /**
     * 一键提醒
     *
     * @Title:
     * @MethodName: reminderStudent
     * [id, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 14:37
     */
    @Override
    public Boolean reminderStudent(Long id, Long userId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), MessageUtils.message("course.notice.id.not.blank"));
        //筛选未确认公告的学生
        LambdaUpdateWrapper<CourseNoticeStudentLogEntity> query = new LambdaUpdateWrapper<>();
        query.eq(CourseNoticeStudentLogEntity::getIsConfirm, YesNoNumberEnum.NO.getCode())
            .eq(CourseNoticeStudentLogEntity::getContentId, id);
        List<CourseNoticeStudentLogEntity> list = courseNoticeStudentLogService.list(query);
        if (CollectionUtils.isEmpty(list)) {
            return true;
        }

        // TODO 新奇助手一键提醒
        if (list.size() > 0) {
            assistantListen.sentNoticeRemind(id, list);
        }

        return true;
    }

    /**
     * 定时发布
     *
     * @Title:
     * @MethodName: releaseNotice
     * []
     * @Return boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/22 17:57
     */
    @Override
    public boolean releaseNotice() {
        //查询定时未发布
        LambdaUpdateWrapper<CourseNoticeEntity> query = new LambdaUpdateWrapper<>();
        query.eq(CourseNoticeEntity::getTiming, YesNoNumberEnum.YES.getCode())
            .eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.NO.getCode())
            .le(CourseNoticeEntity::getReleaseTime, LocalDateTime.now());
        List<CourseNoticeEntity> list = this.baseMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return true;
        }
        //筛选需要发布
        List<Long> releaseIds = new ArrayList<>();
        List<CourseNoticeEntity> releaseList = new ArrayList<>();
        list.forEach(item -> {
            releaseIds.add(item.getId());
            item.setStatus(YesNoNumberEnum.YES.getCode());
            releaseList.add(item);
        });

        //发布
        if (CollectionUtils.isNotEmpty(releaseIds)) {
            courseContentClient.update(YesNoNumberEnum.YES.getCode(), CourseContentTypeEnum.NOTICE.getCode(), releaseIds);
            //发布
            LambdaUpdateWrapper<CourseNoticeEntity> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(CourseNoticeEntity::getStatus, YesNoNumberEnum.YES.getCode())
                .in(CourseNoticeEntity::getId, releaseIds);

            this.update(wrapper);
            //TODO
            for (Long contentId : releaseIds) {
                // 新奇助手发送消息
                assistantListen.releaseCourseContent(contentId, AssistantMessageTypeEnum.NOTICE);
            }
        }
        //同步班级设置
        synchroClass(releaseList);
        return true;
    }

    /**
     * 班级添加成员 -- 公告同步
     *
     * @Title:
     * @MethodName: classAddMembers
     * [classId, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/30 13:47
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean classAddMembers(Long classId, Long userId) {
        //查询未发布
        LambdaQueryWrapper<CourseNoticeEntity> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(CourseNoticeEntity::getClassId, classId)
            .eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.NO.getCode());
        List<CourseNoticeEntity> unpublished = this.baseMapper.selectList(wrapper1);
        //查询进行中
        List<CourseNoticeEntity> inProgress = this.baseMapper.selectList(wrapper(classId));

        List<CourseNoticeEntity> list = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(unpublished)) {
            list.addAll(unpublished);
        }
        if (CollectionUtils.isNotEmpty(inProgress)) {
            list.addAll(inProgress);
        }
        if (CollectionUtils.isEmpty(list)) {
            return true;
        }
        courseNoticeStudentLogService.synchroStudent(list, classId, userId);
        return true;
    }

    /**
     * 班级退出成员 -- 公告同步
     *
     * @Title:
     * @MethodName: classRemoveMembers
     * [classId, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/30 13:47
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean classRemoveMembers(Long classId, Long userId) {
        //查询未发布
        LambdaQueryWrapper<CourseNoticeEntity> query = new LambdaQueryWrapper<>();
        query.eq(CourseNoticeEntity::getClassId, classId)
            .eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.NO.getCode());
        List<CourseNoticeEntity> list = this.baseMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return true;
        }
        List<Long> idList = list.stream().map(CourseNoticeEntity::getId).collect(Collectors.toList());

        LambdaQueryWrapper<CourseNoticeStudentLogEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseNoticeStudentLogEntity::getStudentId, userId)
            .in(CourseNoticeStudentLogEntity::getContentId, idList);

        courseNoticeStudentLogService.remove(wrapper);
        return true;
    }

    /**
     * 班级成员身份变更 -- 公告同步
     *
     * @Title:
     * @MethodName: classChangeIdentity
     * [classId, userId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/4/3 16:04
     */
    @Override
    public Boolean classChangeIdentity(Long classId, Long userId) {
        //查询未发布，进行中
        LambdaQueryWrapper<CourseNoticeEntity> query = new LambdaQueryWrapper<>();
        query.eq(CourseNoticeEntity::getClassId, classId)
            .and(y -> y.eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.NO.getCode()).or()
                .and(n -> n.eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.YES.getCode())
                    .gt(CourseNoticeEntity::getEndTime, LocalDateTime.now())));
        List<CourseNoticeEntity> list = this.baseMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return true;
        }
        List<Long> idList = list.stream().map(CourseNoticeEntity::getId).collect(Collectors.toList());
        if (R.as(classStudentClient.checkIsStudent(classId, userId))) {
            //学生/课代表 -- 添加学生记录
            courseNoticeStudentLogService.synchroStudent(list, classId, userId);
            return true;
        } else {
            //老师、助教 -- 移除学生记录
            LambdaQueryWrapper<CourseNoticeStudentLogEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(CourseNoticeStudentLogEntity::getStudentId, userId)
                .in(CourseNoticeStudentLogEntity::getContentId, idList);

            courseNoticeStudentLogService.remove(wrapper);
        }
        return true;
    }

    /**
     * TODO
     * 公告参与未参与统计
     */
    @Override
    public AssistantTotalResultDTO selectNoticeTakeTotal(Long contentId) {
        CourseNoticeEntity entity = this.baseMapper.selectById(contentId);
        if (Objects.nonNull(entity)) {
            return this.baseMapper.selectNoticeTakeTotal(contentId, entity.getIsConfirm());
        } else {
            return new AssistantTotalResultDTO();
        }
    }

    /**
     * 公告已读未读统计
     */
    @Override
    public List<StudentInfoResultDTO> selectNoticeReadTakeStudent(Long contentId, Integer isRead) {
        return this.baseMapper.selectNoticeReadTakeStudent(contentId, isRead);
    }

    /**
     * 公告已确认未确认统计
     */
    @Override
    public List<StudentInfoResultDTO> selectNoticeConfirmTakeStudent(Long contentId, Integer isConfirm) {
        return this.baseMapper.selectNoticeConfirmTakeStudent(contentId, isConfirm);
    }

    /**
     * 进行中公告查询列表
     */
    @Override
    public List<CourseNoticeEntity> selectNoticeRunningList(Long classId) {
        return this.baseMapper.selectList(wrapper(classId));
    }

    /**
     * 进行中的查询
     *
     * @param classId
     * @return
     */
    private LambdaQueryWrapper<CourseNoticeEntity> wrapper(Long classId) {
        LambdaQueryWrapper<CourseNoticeEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseNoticeEntity::getClassId, classId)
            .eq(CourseNoticeEntity::getStatus, YesNoNumberEnum.YES.getCode())
            .gt(CourseNoticeEntity::getEndTime, LocalDateTime.now());
        return wrapper;
    }

    /**
     * 同步班级
     *
     * @param item
     */
    private void synchroClass(CourseNoticeEntity item) {
        if (Objects.isNull(item)) {
            return;
        }
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), item.getSynchro())) {
            if (StringUtils.isNotEmpty(item.getClassIds())) {
                List<CourseNoticeEntity> list = new ArrayList<>();
                String[] classIds = item.getClassIds().split(",");
                for (String classId : classIds) {
                    CourseNoticeEntity entity = courseNoticeConvert.newEntity(item);
                    entity.setId(null);
                    entity.setClassId(Long.valueOf(classId));
                    entity.setClassIds(StringUtils.EMPTY);
                    entity.setSynchro(YesNoNumberEnum.NO.getCode());
                    entity.setCreateBy(item.getCreateBy());
                    list.add(entity);
                }
                if (CollectionUtils.isNotEmpty(list)) {
                    this.saveBatch(list);
                    //同步主表
                    this.synchroContentNotice(list.stream().map(courseNoticeConvert::convert).collect(Collectors.toList()));
                    //同步学生设置
                    courseNoticeStudentLogService.synchroStudent(list);
                    //TODO
                    if (Objects.equals(YesNoNumberEnum.YES.getCode(), list.get(0).getStatus())) {
                        Spring.committed(() -> list.forEach(noticeEntity -> assistantListen.releaseCourseContent(noticeEntity.getId(), AssistantMessageTypeEnum.NOTICE)));
                    }
                }
            }
        }
    }

    /**
     * 同步班级
     *
     * @param noticeList
     */
    private void synchroClass(List<CourseNoticeEntity> noticeList) {
        if (CollectionUtils.isEmpty(noticeList)) {
            return;
        }
        List<CourseNoticeEntity> list = new ArrayList<>();
        noticeList.forEach(item -> {
            if (Objects.equals(YesNoNumberEnum.YES.getCode(), item.getSynchro())) {
                if (StringUtils.isNotEmpty(item.getClassIds())) {
                    String[] classIds = item.getClassIds().split(",");
                    for (String classId : classIds) {
                        CourseNoticeEntity entity = courseNoticeConvert.newEntity(item);
                        entity.setId(null);
                        entity.setClassId(Long.valueOf(classId));
                        entity.setClassIds(StringUtils.EMPTY);
                        entity.setSynchro(YesNoNumberEnum.NO.getCode());
                        entity.setCreateBy(item.getCreateBy());
                        list.add(entity);
                    }
                }
            }
        });
        if (CollectionUtils.isNotEmpty(list)) {
            this.saveBatch(list);
            //同步学生设置
            courseNoticeStudentLogService.synchroStudent(list);
            // 同步主表
            this.synchroContentNotice(list.stream().map(courseNoticeConvert::convert).collect(Collectors.toList()));
            //TODO
            list.forEach(entity -> {
                if (Objects.equals(YesNoNumberEnum.YES.getCode(), entity.getStatus())) {
                    // 新奇助手发送消息
                    assistantListen.releaseCourseContent(entity.getId(), AssistantMessageTypeEnum.NOTICE);
                }
            });
        }
    }

    /**
     * 同步主表 -- 公告
     *
     * @param notice
     */
    //TODO
    @Override
    public void synchroContentNotice(CourseNoticeResultDTO notice) {
        CourseContentCreateDTO create = new CourseContentCreateDTO();
        //存在同步到其他班级，属于同课程同步，操作人都是当前用户，即班级ID跟随讨论，学校课程则是固定的
        create.setUserId(notice.getCreateBy());
        create.setClassId(notice.getClassId());
        create.setContentId(notice.getId());
        create.setType(CourseContentTypeEnum.NOTICE.getCode());
        create.setTitle(notice.getTitle());
        create.setContent(notice.getContent());
        create.setChapterId(notice.getChapterId());
        create.setReleaseTime(notice.getReleaseTime());
        create.setStatus(notice.getStatus());
        create.setEndTime(notice.getEndTime());
        CourseContentResultDTO checkContent = R.as(courseContentClient.selectByContentIdAndType(notice.getId(), CourseContentTypeEnum.NOTICE.getCode()));

        if (Objects.isNull(checkContent)) {
            courseContentClient.saveContent(create);
        } else {
            CourseContentUpdateDTO update = new CourseContentUpdateDTO();
            //存在同步到其他班级，属于同课程同步，操作人都是当前用户，即班级ID跟随讨论，学校课程则是固定的
            update.setUserId(notice.getCreateBy());
            update.setClassId(notice.getClassId());
            update.setContentId(notice.getId());
            update.setType(CourseContentTypeEnum.NOTICE.getCode());
            update.setTitle(notice.getTitle());
            update.setContent(notice.getContent());
            update.setChapterId(notice.getChapterId());
            update.setReleaseTime(notice.getReleaseTime());
            update.setStatus(notice.getStatus());
            update.setEndTime(notice.getEndTime());
            update.setId(checkContent.getId());
            courseContentClient.updateById(checkContent.getId(), update);
        }
    }

    /**
     * 同步主表 -- 公告
     *
     * @param noticeList
     */
    @Async("threadPoolTaskExecutor")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void synchroContentNotice(List<CourseNoticeResultDTO> noticeList) {
        if (CollectionUtils.isEmpty(noticeList)) {
            return;
        }
        List<CourseContentCreateDTO> saveList = new ArrayList<>();
        List<CourseContentUpdateDTO> updateList = new ArrayList<>();

        List<Long> contentIds = noticeList.stream().map(CourseNoticeResultDTO::getId).collect(Collectors.toList());
        List<CourseContentResultDTO> contentResultList = R.as(courseContentClient.findContentListByIdsAndType(contentIds, CourseContentTypeEnum.NOTICE.getCode()));

        Map<Long, CourseContentResultDTO> entityMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(contentResultList)) {
            contentResultList.forEach(entity -> {
                entityMap.put(entity.getContentId(), entity);
            });
        }

        noticeList.forEach(notice -> {
            CourseContentCreateDTO create = new CourseContentCreateDTO();
            create.setUserId(notice.getCreateBy());
            create.setClassId(notice.getClassId());
            create.setContentId(notice.getId());
            create.setType(CourseContentTypeEnum.NOTICE.getCode());
            create.setTitle(notice.getTitle());
            create.setContent(notice.getContent());
            create.setReleaseTime(notice.getReleaseTime());
            create.setStatus(notice.getStatus());
            create.setEndTime(notice.getEndTime());

            CourseContentResultDTO checkContent = entityMap.get(notice.getId());
            if (Objects.nonNull(checkContent)) {
                CourseContentUpdateDTO update = new CourseContentUpdateDTO();
                update.setUserId(checkContent.getUserId());
                update.setContent(checkContent.getContent());
                update.setTitle(checkContent.getTitle());
                update.setType(CourseContentTypeEnum.NOTICE.getCode());
                update.setClassId(checkContent.getClassId());
                update.setChapterId(checkContent.getChapterId());
                update.setStatus(YesNoNumberEnum.YES.getCode());
                update.setReleaseTime(checkContent.getReleaseTime());
                update.setId(checkContent.getId());
                updateList.add(update);
            } else {
                saveList.add(create);
            }
        });
        if (CollectionUtils.isNotEmpty(saveList)) {
            courseContentClient.saveBatchContent(saveList);
        }
        if (CollectionUtils.isNotEmpty(updateList)) {
            courseContentClient.updateBatch(updateList);
        }
    }

}
